package com.xuecheng.ucenter.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.utils.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xuecheng.ucenter.feignClient.CheckCodeServiceClient;
import com.xuecheng.ucenter.mapper.XcMenuMapper;
import com.xuecheng.ucenter.mapper.XcUserMapper;
import com.xuecheng.ucenter.model.dto.AuthParamsDto;
import com.xuecheng.ucenter.model.dto.XcUserExt;
import com.xuecheng.ucenter.model.po.XcMenu;
import com.xuecheng.ucenter.model.po.XcUser;
import com.xuecheng.ucenter.service.AuthService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Component
public class UserServiceImpl implements UserDetailsService {

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private XcMenuMapper xcMenuMapper;


    /**
     * 实现原本Spring Security里的UserDetailService方法，让他走我们自定义的认证方法
     *
     * 如果要实现统一接口的话，就要更改传过来的参数，密码登录下，s只是一个username，如果要实现扫码登陆，手机验证码登录那就要传递其他参数
     * 因此，我们创建一个实体类用来定义可能传过来的参数，定义传过来的登陆方式状态码，根据状态码get所需要的请求实现不同的认证方式
     * 但由于String类型的参数不可变因此要转为JSON格式
     * @param s username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        AuthParamsDto authParamsDto = null;

        try {
            authParamsDto = JSON.parseObject(s, AuthParamsDto.class);
        } catch (Exception e) {
            log.error("传入的参数异常");
        }

        String authType = authParamsDto.getAuthType();
        System.out.println(authType);

        AuthService bean = applicationContext.getBean(authType, AuthService.class);
        XcUserExt xcUserExt = bean.execute(authParamsDto);

        UserDetails details = getPrincipal(xcUserExt);


        return details;
    }

    /**
     * 获取用户权限信息
     * @param xcUserExt
     * @return
     */
    public UserDetails getPrincipal(XcUserExt xcUserExt){
        String password = xcUserExt.getPassword();
        String id = xcUserExt.getId();
        String[] authorities=null;
        List<XcMenu> xcMenus = xcMenuMapper.selectPermissionByUserId(id);
        if (xcMenus.size()>0){
            List<String> permissions=new ArrayList<>();
            xcMenus.stream().forEach(xcMenu -> {
                String code = xcMenu.getCode();
                permissions.add(code);
            });
            //new String[0]作用是指定返回类型罢了
            authorities=permissions.toArray(new String[0]);
        }

        xcUserExt.setPassword(null);
        String jsonUser = JSON.toJSONString(xcUserExt);

        /**
         * 如果验证通过，由于返回给Spring Security的数据只有用户名，密码，权限，可能不足以支持登陆后的页面展示
         * 比如说展示用户头像等，这些信息不在UserDetail中
         * 想要添加进去可以对Spring Security框架进行修改但是这样有难度，我是做不到
         * 另一种方法，Spring Security之所以能拿到数据是我们返回给他的，那么多返回一些就好，
         * 把需要返回的信息封装成一个实体类，那么把实体类转化为JSON格式放入withUsername中（真是奇思妙想呐）
         * 后期再进行解析
         */
        //把用户名，密码，权限返回封装为UserDetail对象返回
        UserDetails details = User.withUsername(jsonUser).password(password).authorities(authorities).build();
        return details;
    }

}
